package main

import (
    "fmt"
    "strings"
    "bytes"
)

type TableDefine struct {
    Appid        string   `json:"appid,omitempty"`
    TableName    string   `json:"table_name,omitempty"`
    DtLength     int      `json:"dt_length,omitempty"`
    IndexColumns []string `json:"index_columns,omitempty"`
}

func (p TableDefine) Path() string {
    return fmt.Sprintf("%s/%s", p.Appid, p.TableName)
}

const (
    IndexHeader    = "_idx_"
    IndexSeprator  = "||"
    ColumnSeprator = "=="
)

func (ti TableDefine) Contains(column string) bool {
    for _, i := range ti.IndexColumns {
        if i == column {
            return true
        }
    }
    return false
}

func (ti TableDefine) IsSame(td TableDefine) bool {
    if ti.DtLength != td.DtLength || ti.TableName != td.TableName || ti.Appid != td.Appid ||
        len(ti.IndexColumns) != len(td.IndexColumns) {
        return false
    }
    for i, v1 := range ti.IndexColumns {
        if v1 != td.IndexColumns[i] {
            return false
        }
    }
    return true
}

func (ti TableDefine) BuildQueryIndexPrefixes(cond QueryCondition) ([]string, error) {
    var ret []string
    for cn, cv := range cond.index {
        if !ti.Contains(cn) {
            return nil, fmt.Errorf("Index: %s not defined", cn)
        }
        if cond.indexPrefixQuery {
            prefix := fmt.Sprintf("%s%s%s%s%s", IndexHeader, cn, ColumnSeprator, cv, IndexSeprator)
            ret = append(ret, prefix)
        } else {
            prefix := fmt.Sprintf("%s%s%s%s", IndexHeader, cn, ColumnSeprator, cv)
            ret = append(ret, prefix)
        }
    }
    return ret, nil
}

func (ti TableDefine) ExtractKey(raw []byte) []byte {
    s := bytes.Split(raw, []byte(IndexSeprator))
    if len(s) == 2 {
        return s[1]
    }
    return nil
}

//ret is ordered array of []string
func Keys(keys [][]string) []string {
    switch len(keys) {
    case 0:
        return nil
    case 1:
        return keys[0]
    default:
        var ret []string
        if len(keys[0]) == 0 || len(keys[1]) == 0 {
            return nil
        }
        if strings.Compare(keys[0][len(keys[0])-1], keys[1][0]) < 0 {
            return nil
        }
        if strings.Compare(keys[0][0], keys[1][len(keys[1])-1]) > 0 {
            return nil
        }
        for _, va := range keys[0] {
            if Query(va, keys[1]) {
                ret = append(ret, va)
            }
        }
        return Keys(append([][]string{ret}, keys[2:]...))
    }
}

func Query(v string, data []string) bool {
    if l := len(data); l != 0 {
        i := strings.Compare(v, data[l/2])
        switch {
        case i == 0:
            return true
        case i > 0:
            return Query(v, data[l/2+1:l])
        case i < 0:
            return Query(v, data[0:l/2])
        }
    }
    return false
}

func (ti TableDefine) BuildIndex(row Row) ([]string, error) {
    var ret []string
    for cn, cv := range row.Index {
        if !ti.Contains(cn) {
            return []string{cv}, fmt.Errorf("Index: %s not defined", cn)
        }
        //check if there is IndexSeprator,IndexHeader ColumnSeprator in Key
        for _, s := range []string{IndexHeader, IndexSeprator, ColumnSeprator} {
            if strings.Contains(cv, s) {
                return nil, fmt.Errorf("reserved word %s in Key %s", s, cn)
            }
        }
        ret = append(ret, fmt.Sprintf("%s%s%s%s%s", IndexHeader, cn, ColumnSeprator,
            cv, IndexSeprator, row.Key))
    }
    //return indexed columns
    return ret, nil
}

type Partition struct {
    dt        string
    partition string
}

func NewPartition(dir string) (*Partition, error) {
    ss := strings.Split(dir, "-")
    if len(ss) != 2 {
        return nil, fmt.Errorf("wrong dir type")
    }
    return &Partition{ss[0], ss[1]}, nil
}

func (p Partition) IsNil() bool {
    return p.dt == "" && p.partition == ""
}

func (p Partition) Filter(partition, dtPrefix string) bool {
    ret := strings.HasPrefix(p.dt, dtPrefix)
    if partition != "" {
        return p.partition == partition && ret
    }
    return ret
}

func (p Partition) StoreName() string {
    return fmt.Sprintf("%s-%s", p.partition, p.dt)
}

type Row struct {
    Key   string            `json:"Key,omitempty"`
    Value string            `json:"Value,omitempty"`
    Index map[string]string `json:"Index,omitempty"`
}

type RowData struct {
    Partition string            `json:"partition,omitempty"`
    Dt        string            `json:"dt,omitempty"`
    Result    map[string]string `json:"result,omitempty"`
    Keys      []string          `json:"keys,omitempty"`
}
